package campus.aiit.ssl.manage.cron;

import campus.aiit.ssl.dao.DeviceDao;
import campus.aiit.ssl.dao.InstructDao;
import campus.aiit.ssl.dao.InstructTemplateDao;
import campus.aiit.ssl.dao.TimedTaskDao;
import campus.aiit.ssl.domain.model.Device;
import campus.aiit.ssl.domain.model.TimedTask;
import campus.aiit.ssl.manage.modbus.ModBusCommand;
import campus.aiit.ssl.util.ContextUtil;
import campus.aiit.ssl.util.RoleUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.*;
import java.util.*;
import java.util.concurrent.ScheduledFuture;

/**
 * @author ihaokun
 * @date 2019/12/25 16:42
 */
@Slf4j
@Component
public class ManualTaskAction {
  @Resource private TaskScheduler taskScheduler;
  @Resource private TimedTaskDao taskDao;
  private Map<TimedTask, ScheduledFuture<?>> taskMap = new IdentityHashMap<>();

  /*@Scheduled(cron = "00 6 15 * * *")
  private void test(){
    System.out.println("launch");
    taskScheduler.schedule(
        new RunnableTask("run task"), new CronTrigger("0 7 15 * * *"));
  }*/

  @Scheduled(cron = "0 0 0 * * *")
  private void init(){
    log.info("Timer Init Start");
    taskMap.clear();//clear memory data structure
    taskDao.initAllStatus();//set data table all rows status = 0
    List<TimedTask> tasks = taskDao.selectAll();
    tasks.forEach(task -> {
      ScheduledFuture<?> future = taskScheduler.schedule(new RunnableTask(task), task.getPerformTime());
      taskMap.put(task, future);
    });
  }
  public boolean addTask(TimedTask task) {
    task.setUserId(ContextUtil.userId());
    taskDao.insertSelective(task);
    if (Duration.between(LocalTime.now(), task.getPerformTime().toLocalTime()).isNegative()){
      log.info("before now, will init on tomorrow");
    } else {
      ScheduledFuture<?> future = taskScheduler.schedule(new RunnableTask(task), task.getPerformTime().toLocalTime().atDate(LocalDate.now()).atZone(ZoneId.systemDefault()).toInstant());
      taskMap.put(task, future);
    }
    return true;
  }
  public boolean cancelTask(long id){
    // singletonList delete the item & task DAO delete the item
    TimedTask timedTask = null;
    for (TimedTask task : taskMap.keySet()) {
      if (task.getId() == id){
        taskMap.get(task).cancel(true);
        timedTask = task;
      }
    }
    Objects.requireNonNull(timedTask);
    taskMap.remove(timedTask);
    taskDao.deleteByPrimaryKey(id);
    return true;
  }
  public List<TimedTask> listByUser() throws Throwable {
    List<TimedTask> result = new ArrayList<>();
    String userId = RoleUtil.currentUserIdOfDao();
    if (userId == null) {
      return Arrays.asList((TimedTask[]) taskMap.keySet().toArray());
    }
    taskMap.keySet().forEach(timedTask -> {
      if (timedTask.getUserId().equals(userId)){
        result.add(timedTask);
      }
    });
    return result;
  }
  /*private void updateTask(TimedTask task){
    TimedTask rawTask = taskDao.selectByPrimaryKey(task.getId());
    if (taskMap.containsKey(rawTask)){
      taskMap.remove(rawTask);
      ScheduledFuture<?> future = taskScheduler.schedule(new RunnableTask(task), task.getPerformTime());
      taskMap.put(task, future);
    }
    taskDao.updateByPrimaryKeySelective(task);
  }*/

  @Resource private DeviceDao deviceDao;
  @Resource private InstructTemplateDao templateDao;
  @Resource private InstructDao instructDao;

  @Resource private ModBusCommand cmd;
  class RunnableTask implements Runnable {

    private String message;
    private TimedTask timedTask;

    public RunnableTask(TimedTask timedTask) {
      this.timedTask = timedTask;
    }

    @Override
    public void run() {
      System.out.println(message);

      //executed, set status 1
      timedTask.setStatus((byte) 1);
      taskDao.updateStatus(timedTask.getId());

      Device device = deviceDao.selectByPrimaryKey(timedTask.getDeviceId());
      Long templateId = templateDao.selectIdByForeignKeys(device.getTypeId(), device.getModelId());
      String instructValue = instructDao.selectValue(templateId, timedTask.getInstructName());

      cmd.infrared(device.getIpAddress(), device.getPort(), device.getAddressCode(), instructValue);
    }
  }
}